import {
  CSGroup,
  CSLineSegments,
  CSMesh,
  IStreamlineData,
  RENDERORDER,
  ThickLine2,
} from '@amcax/base';
import whiteDisc from '@app-base/assets/textures/white_disc.png';
import {
  BufferAttribute,
  BufferGeometry,
  Color,
  DoubleSide,
  Float32BufferAttribute,
  GLSL3,
  InterleavedBufferAttribute,
  MeshBasicMaterial,
  Points,
  ShaderMaterial,
  TextureLoader,
  Vector2,
  Vector3Tuple,
} from 'three-legacy';
import { LineGeometry } from 'three-legacy/examples/jsm/lines/LineGeometry';
import { LineMaterial } from 'three-legacy/examples/jsm/lines/LineMaterial';
import { getColorByData } from './color';
import { GlobalLineMaterial } from './materials';

/**
 * 通过dxid,生成field的dxid;用于添加和删除
 * @param dxid
 * @returns field的dxid
 */
export function makeFieldDxid(dxid: string) {
  return `${dxid}:field`;
}

export const SuffixWireframe = ':wireframe';

/**
 * 通过dxid,生成wireframe的dxid;用于添加和删除
 * @param dxid
 * @returns wireframe的dxid
 */
export function makeWireframeDxid(dxid: string) {
  return `${dxid}${SuffixWireframe}`;
}

/**
 * 生成field的mesh
 * @param id 模型id
 * @param modelGeometry 在渲染场景中的模型Geometry
 * @param vColors 顶点颜色
 * @param itemSize 每项颜色的长度
 * @returns 生成的mesh
 */
export function makeFieldMesh(
  id: string,
  modelGeometry: BufferGeometry,
  vColors: number[],
  itemSize: number,
) {
  const material = new MeshBasicMaterial({
    vertexColors: true,
    side: DoubleSide,
    flatShading: true,
  });
  const colors = new BufferAttribute(new Float32Array(vColors), itemSize);
  modelGeometry.attributes.color = colors;
  const mesh = new CSMesh({
    geometry: modelGeometry,
    material: material,
    modelDxid: makeFieldDxid(id), // 与clearFields保持一致
    visibility: true,
    renderOrder: RENDERORDER.FACE,
  });
  return mesh;
}

/**
 * 生成wireframe的line(LineSegments)
 * @param id wireframe id,比如: 5bd5bce4-eb6f-4294-b7ba-5caeb32e636e:wireframe
 * @param points 模型的点二维数组
 * @param wires points的index数组，长度为短边数目的两倍
 * @returns 生成的mesh
 */
export function makeWireframeLine(
  id: string,
  position: BufferAttribute | InterleavedBufferAttribute,
  wires: number[],
) {
  const geometry = new BufferGeometry();
  geometry.attributes.position = position;
  geometry.setIndex(wires);
  const mesh = new CSLineSegments({
    geometry,
    material: GlobalLineMaterial,
    modelDxid: id,
    renderOrder: RENDERORDER.LINE_H,
  });
  return mesh;
}

/**
 * 生成流线的mesh，将流线和箭头都放在一个group中
 * @param item 流线数据
 * @param colormap 颜色表
 * @param linewidth 线宽
 * @param arrowlength 箭头长度
 * @returns 包含流线和箭头的group
 */
export function makeStreamlineMesh(
  item: IStreamlineData,
  colormap?: number[][],
  linewidth: number = 5,
  arrowlength: number = 1,
) {
  const mesh = new CSGroup(item.id);
  const points = item.points;
  const polylines = item.polylines;
  const arrows = item.arrows;

  const { colors: vColors, itemSize } = getColorByData(item.colors, colormap);
  const lineMaterial = new LineMaterial({
    linewidth: linewidth,
    vertexColors: true,
  });
  if (vColors.length <= 0) {
    lineMaterial.color = new Color(0x000000);
  }
  // 生成流线
  polylines?.forEach((polyline) => {
    const lineGeometry = new LineGeometry();
    const positions: number[] = [];
    const vertexColors: number[] = [];
    polyline.forEach((pointIndex) => {
      const point = points[pointIndex];
      positions.push(...points[pointIndex]);
      // TODO 二维坐标,应该需要根据参考系(基准面)计算出三维坐标
      if (point.length === 2) {
        positions.push(0);
      }
    });
    if (vColors.length > 0) {
      polyline.forEach((pointIndex) => {
        const start = pointIndex * itemSize;
        const end = start + 3; // 因为lineGeometry中color itemSize默认为3
        vertexColors.push(...vColors.slice(start, end));
      });
    }

    lineGeometry.setPositions(positions);
    if (vColors.length > 0) {
      lineGeometry.setColors(vertexColors); // 看了源码,color itemSize默认为3
    }
    const line = new ThickLine2(lineGeometry, lineMaterial);
    mesh.add(line);
  });

  // 生成箭头
  arrows?.forEach((arrow) => {
    if (arrow.length < 2) {
      return;
    }
    // arrow 由两个点的索引构成,使用这两个点构成一个向量
    const start = points[arrow[0]];
    const end = points[arrow[1]];
    const startV = new Vector2(start[0], start[1]);
    const endV = new Vector2(end[0], end[1]);
    const center = new Vector2(0, 0);
    const arrowVector = new Vector2().subVectors(endV, startV);
    // 计算出箭头的向量(与arrowVector的夹角是45度)
    const leftVector = arrowVector
      .clone()
      .rotateAround(center, Math.PI / 4)
      .setLength(arrowlength);
    const rightVector = arrowVector
      .clone()
      .rotateAround(center, -Math.PI / 4)
      .setLength(arrowlength);
    const arrowPoints = [
      new Vector2().subVectors(arrowVector, leftVector).add(startV).toArray(),
      end,
      new Vector2().subVectors(arrowVector, rightVector).add(startV).toArray(),
    ];
    const lineGeometry = new LineGeometry();
    const positions: number[] = arrowPoints
      .map((ap) => (ap.length === 2 ? [...ap, 0] : ap))
      .flat();
    const vertexColors: number[] = [];
    if (vColors.length > 0) {
      const start = arrow[1] * itemSize; // 都取end的颜色
      const end = start + 3; // 因为lineGeometry中color itemSize默认为3
      const endColor = vColors.slice(start, end);
      for (let i = 0; i < arrowPoints.length; i++) {
        vertexColors.push(...endColor);
      }
    }

    lineGeometry.setPositions(positions);
    lineGeometry.setColors(vertexColors); // 看了源码,color itemSize默认为3
    const line = new ThickLine2(lineGeometry, lineMaterial);
    mesh.add(line);
  });

  return mesh;
}

export function makeTexturePoints(
  points: Vector3Tuple[],
  color: number = 0xffff00,
) {
  const texture = new TextureLoader().load(whiteDisc);
  const geometry = new BufferGeometry();
  geometry.setAttribute(
    'position',
    new Float32BufferAttribute(
      points.flatMap((p) => p),
      3,
    ),
  );

  const material = new ShaderMaterial({
    glslVersion: GLSL3,
    uniforms: {
      color: { value: new Color(color) },
      pointTexture: { value: texture },
      alphaTest: { value: 0.9 },
    },
    vertexShader: /* glsl */ `
    void main() {
			vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    
      gl_PointSize = 20.0;
      gl_Position = projectionMatrix * mvPosition;
    }
    `,
    fragmentShader: /* glsl */ `
    uniform vec3 color;
    uniform sampler2D pointTexture;
    uniform float alphaTest;
    
    out vec4 fragColor;
    
    void main() {
      fragColor = vec4(color, 1.0);  // 基本颜色
    
      fragColor *= texture2D(pointTexture, gl_PointCoord);  // 使用纹理
    
      // 透明度测试；因为png图片是方形的，圆盘周围会有透明空白，则不渲染
      if (fragColor.a < alphaTest) discard;
    }
    `,
  });

  const pointsMesh = new Points(geometry, material);

  return pointsMesh;
}
